<?xml version='1.0' encoding='utf-8'?>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Pro Git - 简体中文版
</title>
    <meta content="http://www.w3.org/1999/xhtml; charset=utf-8" http-equiv="Content-Type"/>
    <link href="stylesheet.css" type="text/css" rel="stylesheet"/>
    <style type="text/css">
		@page { margin-bottom: 5.000000pt; margin-top: 5.000000pt; }</style>
  </head>
  <body class="calibre">
<h2 id="calibre_toc_38" class="calibre3">权限管理器 Gitosis</h2>

<p class="calibre2">把所有用户的公钥保存在 <code class="calibre9">authorized_keys</code> 文件的做法只能暂时奏效。当用户数量到了几百人的时候，它会变成一种痛苦。每一次都必须进入服务器的 shell，而且缺少对连接的限制——文件里的每个人都对所有项目拥有读写权限。</p>

<p class="calibre2">现在，是时候向广泛使用的软件 Gitosis 求救了。Gitosis 简单的说就是一套用来管理 <code class="calibre9">authorized_keys</code> 文件和实现简单连接限制的脚本。最有意思的是，该软件用来添加用户和设定权限的界面不是网页，而是一个特殊的 Git 仓库。你只需要设定好某个项目；然后推送，Gitosis 就会随之改变服务器设定，酷吧？</p>

<p class="calibre2">Gitosis 的安装算不上傻瓜化，不过也不算太难。用 Linux 服务器架设起来最简单——以下例子中的服务器使用 Ubuntu 8.10 系统。</p>

<p class="calibre2">Gitosis 需要使用部分 Python 工具，所以首先要安装 Python 的 setuptools 包，在 Ubuntu 中名为 python-setuptools：</p>

<pre class="calibre8"><code class="calibre9">$ apt-get install python-setuptools
</code></pre>

<p class="calibre2">接下来，从项目主页克隆和安装 Gitosis：</p>

<pre class="calibre8"><code class="calibre9">$ git clone git://eagain.net/gitosis.git
$ cd gitosis
$ sudo python setup.py install
</code></pre>

<p class="calibre2">这会安装几个 Gitosis 用的可执行文件。现在，Gitosis 想把它的仓库放在 <code class="calibre9">/home/git</code>，倒也可以。不过我们的仓库已经建立在 <code class="calibre9">/opt/git</code> 了，这时可以创建一个文件连接，而不用从头开始重新配置：</p>

<pre class="calibre8"><code class="calibre9">$ ln -s /opt/git /home/git/repositories
</code></pre>

<p class="calibre2">Gitosis 将为我们管理公钥，所以当前的文件需要删除，以后再重新添加公钥，并且让 Gitosis 自动控制 <code class="calibre9">authorized_keys</code> 文件。现在，把 <code class="calibre9">authorized_keys</code>文件移走：</p>

<pre class="calibre8"><code class="calibre9">$ mv /home/git/.ssh/authorized_keys /home/git/.ssh/ak.bak
</code></pre>

<p class="calibre2">然后恢复 'git' 用户的 shell，假设之前把它改成了 <code class="calibre9">git-shell</code> 命令。其他人仍然不能通过它来登录系统，不过这次有 Gitosis 帮我们实现。所以现在把 <code class="calibre9">/etc/passwd</code> 文件的这一行</p>

<pre class="calibre8"><code class="calibre9">git:x:1000:1000::/home/git:/usr/bin/git-shell
</code></pre>

<p class="calibre2">恢复成:</p>

<pre class="calibre8"><code class="calibre9">git:x:1000:1000::/home/git:/bin/sh
</code></pre>

<p class="calibre2">现在就可以初始化 Gitosis 了。需要通过自己的公钥来运行 <code class="calibre9">gitosis-init</code>。如果公钥不在服务器上，则必须复制一份：</p>

<pre class="calibre8"><code class="calibre9">$ sudo -H -u git gitosis-init &lt; /tmp/id_dsa.pub
Initialized empty Git repository in /opt/git/gitosis-admin.git/
Reinitialized existing Git repository in /opt/git/gitosis-admin.git/
</code></pre>

<p class="calibre2">这样该公钥的拥有者就能修改包含着 Gitosis 设置的那个 Git 仓库了。然后手动将这个新的控制仓库中的  <code class="calibre9">post-update</code> 脚本加上执行权限。</p>

<pre class="calibre8"><code class="calibre9">$ sudo chmod 755 /opt/git/gitosis-admin.git/hooks/post-update
</code></pre>

<p class="calibre2">万事俱备了。如果设定过程没出什么差错，现在可以试一下用初始化 Gitosis 公钥的拥有者身份 SSH 进服务器。看到的结果应该和下面类似：</p>

<pre class="calibre8"><code class="calibre9">$ ssh git@gitserver
PTY allocation request failed on channel 0
fatal: unrecognized command 'gitosis-serve schacon@quaternion'
  Connection to gitserver closed.
</code></pre>

<p class="calibre2">说明 Gitosis 认出了该用户的身份，但由于没有运行任何 Git 命令所以它切断了连接。所以，现在运行一个确切的 Git 命令——克隆 Gitosis 的控制仓库：</p>

<pre class="calibre8"><code class="calibre9"># 在自己的电脑上
$ git clone git@gitserver:gitosis-admin.git
</code></pre>

<p class="calibre2">得到一个名为 <code class="calibre9">gitosis-admin</code> 的目录，主要由两部分组成：</p>

<pre class="calibre8"><code class="calibre9">$ cd gitosis-admin
$ find .
./gitosis.conf
./keydir
./keydir/scott.pub
</code></pre>

<p class="calibre2"><code class="calibre9">gitosis.conf</code> 文件是用来设置用户、仓库和权限的控制文件。<code class="calibre9">keydir</code> 目录则是保存所有具有访问权限用户公钥的地方——每人一个。你 <code class="calibre9">keydir</code> 中的文件名（前例中的 <code class="calibre9">scott.pub</code>）应该有所不同—— Gitosis 从使用 <code class="calibre9">gitosis-init</code> 脚本导入的公钥尾部的描述中获取该名。</p>

<p class="calibre2">看一下 <code class="calibre9">gitosis.conf</code> 的内容，它应该只包含与刚刚克隆的 <code class="calibre9">gitosis-admin</code> 相关的信息：</p>

<pre class="calibre8"><code class="calibre9">$ cat gitosis.conf 
[gitosis]

[group gitosis-admin]
writable = gitosis-admin
members = scott
</code></pre>

<p class="calibre2">它显示用户 <code class="calibre9">scott</code> ——初始化 Gitosis 公钥的拥有者——是唯一能访问 <code class="calibre9">gitosis-admin</code> 项目的人。</p>

<p class="calibre2">现在我们添加一个新的项目。我们将添加一个名为 <code class="calibre9">mobile</code> 的新节段，在这里罗列手机开发团队的开发者以及他们需要访问权限的项目。由于 'scott' 是系统中的唯一用户，我们把它加成唯一的用户，从创建一个叫做 <code class="calibre9">iphone_project</code> 的新项目开始：</p>

<pre class="calibre8"><code class="calibre9">[group mobile]
writable = iphone_project
members = scott
</code></pre>

<p class="calibre2">一旦修改了 <code class="calibre9">gitosis-admin</code> 项目的内容，只有提交并推送至服务器才能使之生效：</p>

<pre class="calibre8"><code class="calibre9">$ git commit -am 'add iphone_project and mobile group'
[master]: created 8962da8: "changed name"
 1 files changed, 4 insertions(+), 0 deletions(-)
$ git push
Counting objects: 5, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 272 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To git@gitserver:/opt/git/gitosis-admin.git
   fb27aec..8962da8  master -&gt; master
</code></pre>

<p class="calibre2">第一次向新工程 <code class="calibre9">iphone_project</code> 的推送需要在本地的版本中把服务器添加为一个 remote 然后推送。从此手动为新项目在服务器上创建纯仓库的麻烦就是历史了—— Gitosis 会在第一次遇到推送的时候自动创建它们：</p>

<pre class="calibre8"><code class="calibre9">$ git remote add origin git@gitserver:iphone_project.git
$ git push origin master
Initialized empty Git repository in /opt/git/iphone_project.git/
Counting objects: 3, done.
Writing objects: 100% (3/3), 230 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@gitserver:iphone_project.git
 * [new branch]      master -&gt; master
</code></pre>

<p class="calibre2">注意到路径被忽略了（加上它反而没用），只有一个冒号加项目的名字—— Gitosis 会为你找到项目的位置。</p>

<p class="calibre2">要和朋友们共同在一个项目上共同工作，就得重新添加他们的公钥。不过这次不用在服务器上一个一个手动添加到 <code class="calibre9">~/.ssh/authorized_keys</code> 文件末端，而是在 <code class="calibre9">keydir</code> 目录为每一个公钥添加一个文件。文件的命名将决定在 <code class="calibre9">gitosis.conf</code> 文件中用户的称呼。现在我们为 John，Josie 和 Jessica 添加公钥：</p>

<pre class="calibre8"><code class="calibre9">$ cp /tmp/id_rsa.john.pub keydir/john.pub
$ cp /tmp/id_rsa.josie.pub keydir/josie.pub
$ cp /tmp/id_rsa.jessica.pub keydir/jessica.pub
</code></pre>

<p class="calibre2">然后把他们都加进 'mobile' 团队，让他们对 <code class="calibre9">iphone_project</code> 具有读写权限：</p>

<pre class="calibre8"><code class="calibre9">[group mobile]
writable = iphone_project
members = scott john josie jessica
</code></pre>

<p class="calibre2">如果你提交并推送这个修改，四个用户将同时具有该项目的读写权限。</p>

<p class="calibre2">Gitosis 也具有简单的访问控制功能。如果想让 John 只有读权限，可以这样做：</p>

<pre class="calibre8"><code class="calibre9">[group mobile]
writable = iphone_project
members = scott josie jessica

[group mobile_ro]
readonly = iphone_project
members = john
</code></pre>

<p class="calibre2">现在 John 可以克隆和获取更新，但 Gitosis 不会允许他向项目推送任何内容。这样的组可以有尽可能有随意多个，每一个包含不同的用户和项目。甚至可以指定某个组为成员，来继承它所有的成员。</p>

<p class="calibre2">如果出现了什么问题，把 <code class="calibre9">loglevel=DEBUG</code> 加入到 <code class="calibre9">[gitosis]</code> 部分或许有帮助（译注：把日志设置到调试级别，记录更详细的信息）。如果你一不小心搞错了配置，失去了推送权限，可以手动修改服务器上的 <code class="calibre9">/home/git/.gitosis</code> 文件—— Gitosis 从该文件读取信息。一次推送会把 <code class="calibre9">gitosis.conf</code> 保存在服务器上。如果你手动编辑该文件，它将在你下次向 <code class="calibre9">gitosis-admin</code> 推送之前它将保持原样。</p>

</body>
</html>
